home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
More Source
/
C⁄C++
/
AIFF DSP v22
/
main_src
/
aiff.c
next >
Wrap
C/C++ Source or Header
|
1995-01-30
|
9KB
|
375 lines
/* general-purpose code for AIFF-based DSP; contains main(). */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aiff.h"
#include "plugin_specific.h"
#include "file_selection.h"
#include "type_conversion.h"
#define BUF_SIZ (1L << 10) /* buffer size, in frames */
#define FREAD( dat ) fread( &(dat), sizeof (dat), 1, inf )
#define SAMDAT( action, file ) \
if (!f##action ( d, buflen*nh.framsiz, 1, file )) \
err( #action"ing sample data" )
typedef struct
{
UCHAR id[4],
si[4];
}
ckhd; /* chunk header: id & size */
typedef struct
{
UCHAR chan[2], /* number of channels */
fram[4], /* number of sample frames */
wdsi[2], /* sample word size in bits */
rate[10]; /* sample rate in frames/sec (80-bit format) */
}
comck; /* Common Chunk */
typedef struct
{
UCHAR offs[4], /* offset */
bksi[4]; /* block size */
}
sndck; /* Sound Chunk beginning */
typedef struct
{
UCHAR frmtyp[4]; /* form type (always 'AIFF') */
}
frmck; /* Form Chunk beginning */
typedef struct
{
ckhd frmhd; /* form chunk header */
frmck frm; /* form chunk beginning */
ckhd comhd; /* common chunk header */
comck com;
ckhd sndhd; /* sound data chunk header */
sndck snd;
}
basicaiffbeg; /* basic AIFF beginning: note that this structure allows storage of Common and Sound Data local chunk types only. */
native_header nh;
void *d; /* audio data buffer */
static FILE *inf, *ouf; /* input and output files */
static basicaiffbeg ba;
void err ( char *errmsg )
{
fprintf( stderr, "FATAL ERROR: %s.\n", errmsg);
exit(0);
}
void warn ( char *warnmsg )
{
fprintf( stderr, "WARNING: %s.\n", warnmsg);
}
/* oddpad() rounds up to nearest even number. This function is needed because if chunk size is odd, there is an uncounted zero pad byte. */
long oddpad( long x )
{
return x + (x & 1);
}
long min( long a, long b )
{
return a < b ? a : b;
}
void open_inf( void )
{
#define INFSTR_LEN 300
char infstr[INFSTR_LEN];
if ( GETFSTR( infstr, INFSTR_LEN ) ) err( "getting input file name" );
if ( !(inf = fopen( infstr, "rb" )) ) err( "opening input file" );
}
/* processes information in COMM chunk */
void process_com( ckhd hd )
{
ba.comhd = hd;
if ( i4(hd.si) != sizeof ba.com ) err( "wrong COMM chunk header size" );
if ( !FREAD( ba.com ) ) err( "reading COMM chunk body" );
nh.chan = i2(ba.com.chan);
nh.wdsi = i2(ba.com.wdsi);
nh.fram = i4(ba.com.fram);
nh.rate = convert_fr_IEEE_754( ba.com.rate );
nh.framsiz = ( (nh.wdsi+7) / 8 ) * nh.chan;
printf( "\tsampling rate: %g frames/sec\n"
"\tword size: %d bits\n"
"\tchannels: %d\n"
"\tframe size: %d bytes (derived from word size & channels)\n"
"\tsample frames: %lu\n",
nh.rate, nh.wdsi, nh.chan, nh.framsiz, nh.fram );
}
/* prints information in SSND chunk, seeks past sample data, returns the byte offset of the sample data in the file */
long process_snd( ckhd hd )
{
long samdatpos;
ba.sndhd = hd;
if ( !FREAD( ba.snd ) ) err( "reading SSND chunk body" );
if ( i4(ba.snd.bksi) || i4(ba.snd.offs) )
warn( "blocksize and offset not supported by this program" );
samdatpos = ftell( inf );
if ( fseek( inf, oddpad(i4(hd.si)) - sizeof ba.snd, SEEK_CUR) )
err( "seeking past sample data");
return samdatpos;
}
/* prints information in a text chunk */
void process_txt( ckhd hd )
{
#define TXTBUF_SIZ 80
char s[TXTBUF_SIZ];
long bufpos, cksi;
int buflen;
cksi = oddpad( i4(hd.si) ); /*1*/
printf( "\ttext: \"" );
for (bufpos = 0; bufpos < cksi; bufpos += buflen)
{
buflen = min( cksi - bufpos, TXTBUF_SIZ );
if ( !fread( s, buflen, 1, inf ) ) err( "reading text chunk" );
printf( "%.*s", buflen, s );
}
puts ( "\"" );
}
/* 1. OK to include zero pad as part of string in C */
void process_ckhd( ckhd hd )
{
int i;
printf( "\nFound '%.4s' chunk of size %ld\n", hd.id, i4(hd.si) );
for (i=0; i<4; i++)
if ( hd.id[i] < ' ' || hd.id[i] > '~' ) warn( "illegal ID character" );
if ( hd.id[0] == ' ' ) warn( "illegal leading space in ID" );
}
#define ID_EQ( id0, id1 ) (!strncmp( (char *) id0, id1, 4 ))
/* scan_inf() scans inf, reading in essential header data & finding sample data position (does not actually read sample data). */
void scan_inf ( void )
{
int comfound = 0, sndfound = 0;
long frmck_endpos, samdatpos;
ckhd hd;
if ( !FREAD( ba.frmhd ) || !FREAD( ba.frm ) )
err( "reading FORM header or type" );
if ( !ID_EQ(ba.frmhd.id, "FORM") || !ID_EQ(ba.frm.frmtyp, "AIFF") )
err( "Bad FORM chunk id or type" );
process_ckhd( ba.frmhd );
frmck_endpos = i4(ba.frmhd.si) + sizeof hd;
while ( ftell( inf ) < frmck_endpos )
{
if ( !FREAD( hd ) ) err( "reading next chunk header" );
process_ckhd( hd );
if ID_EQ( hd.id, "COMM" )
{
comfound++;
process_com( hd );
}
else if ID_EQ( hd.id, "SSND" )
{
sndfound++;
samdatpos = process_snd( hd );
}
else if ( ID_EQ( hd.id, "NAME" ) || ID_EQ( hd.id, "AUTH" ) ||
ID_EQ( hd.id, "(c) " ) || ID_EQ( hd.id, "ANNO" ))
process_txt( hd );
else
{
puts( "\tchunk type not used by this program" );
if ( fseek( inf, oddpad(i4(hd.si)), SEEK_CUR ) )
err( "seeking past unused chunk body" );
}
}
puts( "\n" );
if ( fgetc( inf ) != EOF ) warn( "File extends beyond FORM chunk" );
if ( comfound != 1 || sndfound != 1 )
err( "Not exactly 1 COMM and 1 SSND chunk" );
if ( nh.fram * nh.framsiz != i4(ba.sndhd.si) - sizeof ba.snd )
err( "COMM & SSND chunks disagree about sample length" );
if ( fseek( inf, samdatpos, SEEK_SET ) )
err( "seeking to beginning of sample data" );
}
void write_ouf_hd( void )
{
if ( !fwrite( &ba, sizeof ba, 1, ouf ) ) err( "writing to output file" );
}
/* updates (& creates, if necessary) a bar graph of progress */
void prog_report( long bufpos )
{
#define BARMAX 75 /* length of progress report bar */
char barlen;
static char oldbarlen = 0, bar[BARMAX];
if (bufpos == 0)
{
fputs( "Processing...\n", stderr );
memset( bar, '*', BARMAX );
fprintf( stderr, "%.*s\n", BARMAX, bar );
}
else
{
barlen = (double) (bufpos / nh.fram) * BARMAX;
fprintf( stderr, "%.*s", barlen - oldbarlen, bar );
oldbarlen = barlen;
if ( bufpos == nh.fram ) fputc( '\n', stderr );
}
}
void pad( FILE *ouf )
{
if ( (nh.fram * nh.framsiz) % 2 )
if ( fputc( 0, ouf ) == EOF ) err( "padding sample data" );
}
void prepare_ba( plugin_info* plugin )
{
long sample_bytes;
if ( plugin->take_input )
{
sample_bytes = i4(ba.sndhd.si) - sizeof ba.snd; /*1*/
c4( ba.frmhd.si, sizeof ba - sizeof ba.frmhd +
oddpad( i4(ba.sndhd.si) - sizeof ba.snd ) );
}
else
{
sample_bytes = nh.fram * nh.framsiz;
strncpy( (char *) ba.frmhd.id, "FORM", 4 );
strncpy( (char *) ba.comhd.id, "COMM", 4 );
strncpy( (char *) ba.sndhd.id, "SSND", 4 );
c4( ba.frmhd.si, sizeof ba - sizeof ba.frmhd + oddpad(sample_bytes) );
c4( ba.sndhd.si, sizeof ba.snd + sample_bytes );
c4( ba.comhd.si, sizeof ba.com );
strncpy( (char *) ba.frm.frmtyp, "AIFF", 4 );
c2(ba.com.chan, nh.chan);
c2(ba.com.wdsi, nh.wdsi);
c4(ba.com.fram, nh.fram);
convert_to_IEEE_754( nh.rate, ba.com.rate );
c4( ba.snd.offs, 0 );
c4( ba.snd.bksi, 0 );
}
}
/* 1. Don't trust nh in calculating sample_bytes: a plugin might have changed it illegally. Instead, go back and use ba.sndhd.si. */
int main( int argc, char *argv[] )
{
long bufpos, buflen; /* 1,2 */
plugin_info *plugin;
#ifdef THINK_C
Think_C_init( &argv );
#endif
plugin = select_plugin();
if ( plugin->take_input )
{
open_inf();
scan_inf();
}
plugin->init_process();
if ( plugin->make_output )
{
prepare_ba( plugin );
ouf = OPEN_OUF();
write_ouf_hd();
}
if (!(d = malloc( nh.framsiz*BUF_SIZ )))
err( "allocating memory for sample buffer" );
for (bufpos = 0; bufpos < nh.fram; bufpos += buflen )
{
prog_report( bufpos );
buflen = min( nh.fram - bufpos, BUF_SIZ ) ;
if ( plugin->take_input )
{
SAMDAT( read, inf );
byte_reorder( buflen );
}
plugin->process_samdat( buflen );
if ( plugin->make_output )
{
byte_reorder( buflen );
SAMDAT( write, ouf );
}
}
prog_report( bufpos );
if ( plugin->make_output )
{
pad( ouf );
fclose( ouf );
}
if ( plugin->take_input )
fclose( inf );
free( d );
plugin->term_process();
return 0;
}
/*
1. buffer position: frame # where the next buffer will begin.
2. buffer length: # of meaningful frames in the buffer.
buflen == BUF_SIZ on all passes except for the last, when buflen == samlen % BUF_SIZ.
The following is an example for bufpos = 4 and buflen = 2. (In reality these quantities will be much larger.)
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | (sample frames)
|-------| |
buflen |
bufpos
*/